package com.mark.user.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.mark.basic.contsants.BaseConstants;
import com.mark.basic.dto.LoginDto;
import com.mark.basic.jwt.JwtUtils;
import com.mark.basic.jwt.RsaUtils;
import com.mark.basic.jwt.UserInfo;
import com.mark.basic.service.Impl.BaseServiceImpl;
import com.mark.basic.util.*;
import com.mark.org.domain.Wxuser;
import com.mark.org.mapper.WxuserMapper;
import com.mark.system.domain.Menu;
import com.mark.system.mapper.MenuMapper;
import com.mark.system.mapper.PermissionMapper;
import com.mark.user.domain.Logininfo;
import com.mark.user.mapper.LogininfoMapper;
import com.mark.user.service.ILogininfoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.spec.InvalidKeySpecException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author mark
 * @since 2022-04-01
 */
@Service
public class LogininfoServiceImpl extends BaseServiceImpl<Logininfo> implements ILogininfoService {
    @Autowired
    private LogininfoMapper logininfoMapper;
    @Autowired
    private WxuserMapper wxuserMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private MenuMapper menuMapper; // 菜单
    @Autowired
    private PermissionMapper permissionMapper;  // 角色 按钮
    @Value("${jwt.rsa.pri}") // 私钥的文件名
    private String jwtRsaPrivate;

    /**
     * 员工登录
     *
     * @param loginDto
     */
    @Override
    public Map<String, Object> loginAccount(LoginDto loginDto) {
        // 校验是否为空
        if (StringUtils.isEmpty(loginDto.getUsername())
                || StringUtils.isEmpty(loginDto.getPassword())
                || StringUtils.isEmpty(loginDto.getType())) {
            throw new BusinessException("请输入相关信息");
        }
        // 校验用户名和密码是否正确
        Logininfo logininfo = logininfoMapper.loginAccount(loginDto);
        if (logininfo == null) {
            throw new BusinessException("用户名和密码不正确！");
        }
        // 密码比对 获取salt、md5
        String salt = logininfo.getSalt();
        String md5passwprd = MD5Utils.encrypByMd5(salt);
        if (md5passwprd.equals(logininfo.getPassword())) {
            throw new BusinessException("用户名和密码不正确！");
        }

        Map<String, Object> map = loginSuccessHandler(logininfo);
        // 保存token和logininfo到浏览器
        // 获取token和logininfo
//        String token = UUID.randomUUID().toString();
//        redisTemplate.opsForValue().set(token, logininfo, 30, TimeUnit.MINUTES);
//        // 将token和logininfo用map封装
//        Map<String, Object> map = new HashMap<>();
//        map.put("token", token);
//        // 为了数据安全，将盐值和Password重置
//        logininfo.setSalt(null);
//        logininfo.setPassword(null);
//        map.put("logininfo", logininfo);
        return map;

    }

    /**
     * 登录成功之后的业务
     * @param logininfo
     */
    private Map<String, Object> loginSuccessHandler(Logininfo logininfo) {
        UserInfo userInfo = new UserInfo();
        userInfo.setUser(userInfo); // 当前登录人
        // 判断是前端用户还是管理用户
        if (logininfo.getType() == 1){ // 前端用户
            // 前端不用管权限，管理者才需要
            userInfo.setMenus(null);
            userInfo.setMenus(null);
        }else { // 管理者
            // 查询当前登录人的权限
            // 1、菜单权限
            List<Menu> menus = menuMapper.loadMenuByLogininfoId(logininfo.getId());
            // 2、按钮属性
            List<String> permissionSns = permissionMapper.loadPerssionSnByLogininfoId(logininfo.getId());
            // 将有用的权限放入数据中
            userInfo.setMenus(menus);
            userInfo.setPermissions(permissionSns);
        }
        // 将权限信息和登录信息封装成Jwt串
        //获取私钥
        try {
            //获取私钥
            PrivateKey privateKey = RsaUtils
                    .getPrivateKey(FileCopyUtils
                            .copyToByteArray(this.getClass().getClassLoader().getResourceAsStream(jwtRsaPrivate)));
        // 将数据封装成JWT串
            String token = JwtUtils.generateTokenExpireInMinutes(userInfo, privateKey, 30);
            // 将数据传给前台
            Map<String, Object> map = new HashMap<>();
            map.put("token",token);//JWT串  当前登陆人的信息  菜单信息  按钮信息
            //由于我们的前端没有写JWT解密实现，所以我们直接将信息还是原样放回给前端
            logininfo.setSalt(null);
            logininfo.setPassword(null);
            map.put("logininfo",logininfo);
            map.put("menus",userInfo.getMenus());
            map.put("permissions",userInfo.getPermissions());
            return map;
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (InvalidKeySpecException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;

    }

    /**
     * 第三方扫码登录 -- 微信
     *
     * @param params 需要获取 code  binder
     * @return
     */
    @Override
    public AjaxResult loginWeChat(Map<String, String> params) {
//        校验数据
        String code = params.get("code"); //
        String binderUrl = params.get("binderUrl"); // 绑定页面路径
//        根据code获取token，在根据token获取 access_token + openid
// https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
        String ackUrl = BaseConstants.WxConstants.GET_ACK_URL
                .replace("APPID", BaseConstants.WxConstants.APPID)
                .replace("SECRET", BaseConstants.WxConstants.SECRET)
                .replace("CODE", code);
        // 使用工具类HttpClientUtils  转换为json格式的字符串
        String jsonStr = HttpClientUtils.httpGet(ackUrl);
        // 将json格式的字符串转换为字符串  阿里巴巴 JSONObject.（最全面）
        /*
            "access_token":"ACCESS_TOKEN",
            "expires_in":7200,
            "refresh_token":"REFRESH_TOKEN",
            "openid":"OPENID",
            "scope":"SCOPE",
            "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
         */
        JSONObject parseStr = JSONObject.parseObject(jsonStr);
        // 获取access_token + openid
        String access_token = parseStr.getString("access_token");
        String openid = parseStr.getString("openid");
//        判断用户是否已存在（wxuser）
        Wxuser wxUser = wxuserMapper.listByOpenId(openid);
        System.out.println(wxUser);
        // 用户存在
        if (wxUser != null && wxUser.getUser_id() != null) {
//        3、用户存在
//        3.1 用户存在就直接免密登录
            Logininfo logininfo = logininfoMapper.loadByUserId(wxUser.getUser_id());
            Map<String, Object> map = loginSuccessHandler(logininfo);
            return AjaxResult.me().setResultObj(map);
//            if (logininfo != null) { // 如果不为空说明已经关联
//                String token = UUID.randomUUID().toString();
//                // 设置有限时间
//                redisTemplate.opsForValue().set(token, logininfo, 30, TimeUnit.MINUTES);
//                // 将token和logininfo装入map，传给接口再到前端
//                Map<String, Object> map = new HashMap<String, Object>();
//                // 安全起见，将盐值和密码置归
//                map.put("token", token);
//                logininfo.setPassword(null);
//                logininfo.setSalt(null);
//                map.put("logininfo", logininfo);
//                return AjaxResult.me().setResultObj(map);
//            }
        }
//        1、用户存在但未绑定
//        2、用户不存在
        // 获取绑定页面 binderUrl的路径
//          前端跳转到绑定页面时需携带用户名、密码、access_token + openid
        binderUrl = "?access_token=" + access_token + "&openid=" + openid;
        return AjaxResult.me().setSuccess(false).setResultObj(binderUrl);
    }
}
